package com.youtills.core.crypto;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.GeneralSecurityException;

/**
 * Created by IntelliJ IDEA.
 * User: ravi
 * Date: 8/29/12
 * Time: 6:32 PM
 * To change this template use File | Settings | File Templates.
 */
class AesCipher implements ICipher{

    private static final String ALGORITHM_NAME = "AES";
    private static final String DEFAULT_SECRET_KEY = "R@\\/!_cH";

    private static final String CHAR_SET_UTF8 = "UTF8";

    private final String secretKey;

    AesCipher(){
        this(DEFAULT_SECRET_KEY);
    }

   AesCipher( String secretKey) {
        if(secretKey.length() < 8){
            throw new CryptoException("Secret Key for DES must have minimum 8 chracters");
        }
        this.secretKey = secretKey;
   }

   public synchronized String encrypt(String plainText){

       try{
           SecretKey skey = generateKey();
           byte[] raw = skey.getEncoded();
           SecretKeySpec skeySpec = new SecretKeySpec(raw, ALGORITHM_NAME);

           Cipher cipher = Cipher.getInstance(ALGORITHM_NAME);

               // Encryption......
               // Client will encrypt Original Message and then will convert it to Hexadecimal String
               // String -> Byte -> Encrypt -> Byte -> Hexa String -> Pass to Service

           cipher.init(Cipher.ENCRYPT_MODE, skeySpec);

           byte[] encrypted = cipher.doFinal(plainText.getBytes());

           return asHex(encrypted);
       }catch (GeneralSecurityException e) {
           throw new CryptoException(e);
       }

   }

   public synchronized String decrypt(String plainText){

       try{
           SecretKey skey = generateKey();
           byte[] raw = skey.getEncoded();
           Cipher cipher = Cipher.getInstance(ALGORITHM_NAME);
           SecretKeySpec skeySpec = new SecretKeySpec(raw, ALGORITHM_NAME);
            cipher.init(Cipher.DECRYPT_MODE, skeySpec);
            //During the decryption, first Service will convert the
            //Get Hexa String -> Byte -> Decrypt -> Byte -> String
            byte[] encryptedByteArr = hexStringToByteArray(plainText);

            byte[] original = cipher.doFinal(encryptedByteArr);

            return new String(original);
       }catch(GeneralSecurityException e){
           throw new CryptoException(e);
       }

   }

   public static byte[] hexStringToByteArray(String s) {
       int len = s.length();
       byte[] data = new byte[len / 2];
       for (int i = 0; i < len; i += 2) {
           data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                                + Character.digit(s.charAt(i+1), 16));
       }
       return data;
   }

   /**
    * Turns array of bytes into string
    *
    * @param   buf  Array of bytes to convert to hex string
    *
    * @return  Generated hex string
    */

   public static String asHex (byte buf[]) {
       StringBuffer strbuf = new StringBuffer(buf.length * 2);
       int i;

       for (i = 0; i < buf.length; i++) {
       if (((int) buf[i] & 0xff) < 0x10)
       strbuf.append("0");

       strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
       }

       return strbuf.toString();
   }

   private SecretKey generateKey() {
       SecretKey key = new SecretKeySpec(arrayCopy(secretKey.getBytes(),16), ALGORITHM_NAME);
       return key;
   }

   public static byte[] arrayCopy(byte[] orgByteArray, int length){
       byte [] destination = new byte[length];
       if(null != orgByteArray && orgByteArray.length>0){
           if(orgByteArray.length<=length){
               for(int i=0; i<orgByteArray.length; i++){
                   destination[i]=orgByteArray[i];
               }
           }
           else{
               for(int i=0; i<length; i++){
                   destination[i]=orgByteArray[i];
               }
           }
       }
       return destination;
   }

    public CipherType getCypherType() {
        return CipherType.AES;
    }
}
